package scfg.utils.training_set_filter;

import scfg.AlignedSequence;
import scfg.utils.MFECategorizer;

import java.util.*;

public class RandomizedSetSelector {

	private static void printSortedSeq(SortableAlignedSequence seq) {
		System.out.println(seq.fmeasure);
		System.out.println(seq.getAlignedSequence());
	}
	
	public static List<AlignedSequence>[] getEMHTrainingSets(List<AlignedSequence> sequences) {
		List<AlignedSequence>[] arr = (List<AlignedSequence>[]) new ArrayList[3];
		// I hate writing less than 3...
		for (int i=0;i<3;i++)
			arr[i] = new ArrayList<AlignedSequence>();
		List<SortableAlignedSequence> sorted = getSortedList(sequences);
		
		int hardStart = -1;
		int hardEnd = -1;
		int mediumStart = -1;
		int mediumEnd = -1;
		int easyStart = -1;
		int easyEnd = -1;
		for (int i=0;i<sorted.size();i++) {
			double fMeasure = sorted.get(i).fmeasure / 100000;
			if (fMeasure > 0.09 && fMeasure < 0.31) {
				hardEnd = i;
				if (hardStart < 0)
					hardStart = i;
			}
			if (fMeasure > 0.39 && fMeasure < 0.61) {
				mediumEnd = i;
				if (mediumStart < 0)
					mediumStart = i;
			}
			if (fMeasure > 0.69 && fMeasure < 0.91) {
				easyEnd = i;
				if (easyStart < 0)
					easyStart = i;
			}
		}

		System.out.println("Easy" + (easyEnd - easyStart+1));
		System.out.println("Medium" + (mediumEnd - mediumStart+1));
		System.out.println("Hard" + (hardEnd - hardStart+1));
		
		for(int i=hardStart;i<=hardEnd;i++)
			arr[2].add(sorted.get(i).getAlignedSequence());
		
		for(int i=mediumStart;i<=mediumEnd;i++)
			arr[1].add(sorted.get(i).getAlignedSequence());
		
		for(int i=easyStart;i<=easyEnd;i++)
			arr[0].add(sorted.get(i).getAlignedSequence());
		
		return arr;
	}
	
	public static List<List<AlignedSequence>>[] getMultipleTrainingSets(List<AlignedSequence> sequences, int size, int quantity) {
		List<List<AlignedSequence>>[] arr = (List<List<AlignedSequence>>[]) new ArrayList[3];
		for (int i=0;i<3;i++)
			arr[i] = new ArrayList<List<AlignedSequence>>();
		List<SortableAlignedSequence> sorted = getSortedList(sequences);
		
		int hardStart = -1;
		int hardEnd = -1;
		int mediumStart = -1;
		int mediumEnd = -1;
		int easyStart = -1;
		int easyEnd = -1;
		for (int i=0;i<sorted.size();i++) {
			double fMeasure = sorted.get(i).fmeasure / 100000;
//			System.out.println(fMeasure);
			if (fMeasure > 0.09 && fMeasure < 0.31) {
				hardEnd = i;
				if (hardStart < 0)
					hardStart = i;
			}
			if (fMeasure > 0.39 && fMeasure < 0.61) {
				mediumEnd = i;
				if (mediumStart < 0)
					mediumStart = i;
			}
			if (fMeasure > 0.69 && fMeasure < 0.91) {
				easyEnd = i;
				if (easyStart < 0)
					easyStart = i;
			}
		}
		
		System.out.println("Easy size: " + (easyEnd - easyStart + 1));
		System.out.println("Medium size: " + (mediumEnd - mediumStart + 1));
		System.out.println("Hard size: " + (hardEnd - hardStart + 1));
		
		arr[2] = getRandomSet(sorted, size, quantity, hardStart, hardEnd);
		arr[1] = getRandomSet(sorted, size, quantity, mediumStart, mediumEnd);
		arr[0] = getRandomSet(sorted, size, quantity, easyStart, easyEnd);
		
//		int quartileLength = sorted.size()/4;
//		System.out.println("quartile length: " + quartileLength);
//		arr[2] = getRandomSet(sorted, size, quantity, 0, quartileLength);
//		int mid = sorted.size()/2;
//		int giveOrTake = quartileLength>>1;
//		arr[1] = getRandomSet(sorted, size, quantity, mid-giveOrTake, mid+giveOrTake);
//		arr[0] = getRandomSet(sorted, size, quantity, sorted.size()-1-quartileLength, sorted.size()-1);
		return arr;
	}
	
	private static List<List<AlignedSequence>> getRandomSet(List<SortableAlignedSequence> sorted, int size, int quantity, int bottom, int top) {
		List<List<AlignedSequence>> rtn = new ArrayList<List<AlignedSequence>>();
		if (size > 0 && quantity > 0 && bottom <= top && bottom >= 0 && top < sorted.size()) {
			if (size > sorted.size())
				size = sorted.size();
			if (top-bottom + 1 < size)
				size = top - bottom + 1;
			int diff = top-bottom + 1;
			for (int i=0;i<quantity;i++) {
				List<AlignedSequence> curr = new ArrayList<AlignedSequence>();
				while (curr.size() < size) {
					int index = (int)((Math.random() * diff) + bottom);
					if (!myContains(curr, sorted.get(index).getAlignedSequence())) //curr.contains(sorted.get(index)))
						curr.add(sorted.get(index).getAlignedSequence());
				}
				rtn.add(curr);
			}
		}
		return rtn;
	}
	
	public static List<List<AlignedSequence>> getRandomApprovedSet(List<AlignedSequence> sorted, int size, int quantity, int bottom, int top) {
		List<List<AlignedSequence>> rtn = new ArrayList<List<AlignedSequence>>();
		if (size > 0 && quantity > 0 && bottom <= top && bottom >= 0 && top < sorted.size()) {
			if (size > sorted.size())
				size = sorted.size();
			if (top-bottom + 1 < size)
				size = top - bottom + 1;
			int diff = top-bottom + 1;
			for (int i=0;i<quantity;i++) {
				List<AlignedSequence> curr = new ArrayList<AlignedSequence>();
				while (curr.size() < size) {
					int index = (int)((Math.random() * diff) + bottom);
					if (!myContains(curr, sorted.get(index))) //curr.contains(sorted.get(index)))
						curr.add(sorted.get(index));
				}
				rtn.add(curr);
			}
		}
		return rtn;
	}
	
	private static boolean myContains(List<AlignedSequence> list, AlignedSequence perp) {
		boolean contains = false;
		for (AlignedSequence seq : list)
			contains = contains || (seq.getAccessionNumber().equals(perp.getAccessionNumber()) && seq.getName().equals(perp.getName()));
		return contains;
	}
	
	private static List<SortableAlignedSequence> getSortedList(List<AlignedSequence> sequences) {
		List<SortableAlignedSequence> rtn = new ArrayList<SortableAlignedSequence>();
		for (AlignedSequence seq : sequences)
			rtn.add(new SortableAlignedSequence(seq));
		Collections.sort(rtn);
		return rtn;
	}
	
	private static class SortableAlignedSequence implements Comparable<SortableAlignedSequence> {
		
		private AlignedSequence seq;
		private double fmeasure;
		
		public AlignedSequence getAlignedSequence() {
			return seq;
		}
		
		public SortableAlignedSequence(AlignedSequence alignedSequence) {
			this.seq = alignedSequence;
			fmeasure = calcFmeasure();
		}
		
		private double calcFmeasure() {
			String nat = seq.getStructure("refrence").getStructure();
			String pred = seq.getStructure("mfe_gt").getStructure();
			if (nat == null || pred == null)
				return -1;
			return MFECategorizer.compareStructures(nat, pred);
		}

		@Override
		public int compareTo(SortableAlignedSequence that) {
			if (this.fmeasure < 0 && that.fmeasure < 0)
				return 0;
			if (this.fmeasure < 0)
				return 1;
			if (that.fmeasure < 0)
				return -1;
			if (this.fmeasure < that.fmeasure)
				return -1;
			if (this.fmeasure > that.fmeasure)
				return 1;
			return 0;
		}
		
	}
}
